package com.example.server.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.domain.exam.Exam;
import com.example.domain.exam.ExamQuestion;
import com.example.domain.exam.dto.ExamAddDTO;
import com.example.domain.exam.dto.ExamEditDTO;
import com.example.domain.exam.dto.ExamQueryDTO;
import com.example.domain.exam.dto.ExamQuestionDTO;
import com.example.domain.exam.vo.ExamDetailVO;
import com.example.domain.exam.vo.ExamVO;
import com.example.domain.question.Question;
import com.example.domain.question.vo.QuestionVO;
import com.example.manager.ExamCacheManager;
import com.example.mapper.exam.ExamMapper;
import com.example.mapper.exam.ExamQuestionMapper;
import com.example.mapper.question.QuestionMapper;
import com.example.security.exception.ServiceImplException;
import com.example.server.exam.IExamService;
import com.github.pagehelper.PageHelper;
import oj.common.core.contants.Constants;
import oj.common.core.enums.ResultCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper, ExamQuestion> implements IExamService {

    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private  ExamQuestionMapper examQuestionMapper;

    @Autowired
    private ExamCacheManager examCacheManager;
    @Override
    public List<ExamVO> list(ExamQueryDTO examQueryDTO) {
        PageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());
        return examMapper.selectExamList(examQueryDTO);
    }

    @Override
    public String add(ExamAddDTO examAddDTO) {
        //首先先检查是否有重复的竞赛
        //检查时间是不是符合要求
        checkExamSaveParams(examAddDTO,null);
        Exam exam = new Exam();
        BeanUtil.copyProperties(examAddDTO, exam);
        examMapper.insert(exam);
        return exam.getExamId().toString();
    }

    @Override
    public boolean questionAdd(ExamQuestionDTO examQuestionDTO) {
        //首先先对id进行判断，如果是空的话，直接抛出异常了
        if(examQuestionDTO.getExamId()==null) {
            throw new ServiceImplException(ResultCode.EXAM_ID_NOT_EXISTS);
        }
        getExam(examQuestionDTO.getExamId());
        checkExamTime(getExam(examQuestionDTO.getExamId()));
        //接下来就是查询题目了
        //这里是判断这些题目id是否存在。
        List<Question> questionList = questionMapper.selectBatchIds(examQuestionDTO.getQuestionIdSet());
        System.out.println("111111111111");
        if(CollectionUtil.isEmpty(questionList) || questionList.size()<examQuestionDTO.getQuestionIdSet().size()) {
            throw new ServiceImplException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }
        //程序到这里就开始插入数据了
        batchInsertExamQuestion(examQuestionDTO);
        return true;
    }
    @Override
    public ExamDetailVO detail(Long examId) {
        Exam exam = getExam(examId);
        ExamDetailVO examDetailVO = new ExamDetailVO();
        BeanUtil.copyProperties(exam, examDetailVO);
        //查询题目特定的字段。
        List<QuestionVO> questionVOList = examQuestionMapper.selectExamQuestionList(examId);
        if (CollectionUtil.isEmpty(questionVOList)) {
            return examDetailVO;
        }
        examDetailVO.setExamQuestionList(questionVOList);
        return examDetailVO;
    }

    @Override
    public int edit(ExamEditDTO examEditDTO) {
        //这里还需要进行参数检验。
        Exam exam = getExam(examEditDTO.getExamId());
        checkExamTime(exam);
        exam.setTitle(examEditDTO.getTitle());
        //其实这里还需要传的是一个examId，ne代表是的not equal不等于，再加上examId！=null
        //从而判断添加的时候，只查询title模糊查询。
        checkExamSaveParams(examEditDTO,examEditDTO.getExamId());
        exam.setStartTime(examEditDTO.getStartTime());
        exam.setEndTime(examEditDTO.getEndTime());
        return examMapper.updateById(exam);
    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        Exam exam = getExam(examId);
        //这里还需要判断竞赛是否已经开赛
        //在添加题目的时候，也要进行判断和编辑的时候。
        checkExamTime(exam);
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId)
                .eq(ExamQuestion::getQuestionId, questionId));
    }

    @Override
    public int delete(Long examId) {
        Exam exam= getExam(examId);
        checkExamTime(exam);
        //删除竞赛
        //在删除竞赛的时候，由于竞赛不一定有题目，所以要直接返回删除竞赛的结果delete.
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        return examMapper.deleteById(examId);
    }

    @Override
    public int publish(Long examId) {
        Exam exam = getExam(examId);
        //这里要进行判断时间
        if(exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceImplException(ResultCode.EXAM_IS_FINISH);
        }
        Long count = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        if(count==null || count==0) {
            throw new ServiceImplException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }
        exam.setStatus(Constants.PUBLISH_TRUE);
        //这里的话要进行添加到redis中去
        //todo
        examCacheManager.addCache(exam);
        return examMapper.updateById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {
        Exam exam = getExam(examId);
        checkExamTime(exam);
        //感觉这里有点奇怪，如果竞赛已经开始
        //在前端的时候，根本选不到结束时间早于开始时间，这一块判断可以去掉
        //不管是竞赛编辑或者是删除只有当进行发布或者是取消发布的时候才会进行那个从缓存中拿走。
        if(exam.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceImplException(ResultCode.EXAM_STARTED);
        }
        if(exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceImplException(ResultCode.EXAM_IS_FINISH);
        }
        exam.setStatus(Constants.PUBLISH_FALSE);
        //删除缓存相应的数据。
        //todo
        examCacheManager.deleteCache(examId);
        return examMapper.updateById(exam);
    }

    private void checkExamTime(Exam exam) {
        if(exam.getStartTime().isBefore(LocalDateTime.now())) {
            throw  new ServiceImplException(ResultCode.EXAM_STARTED);
        }
    }

    private Exam getExam(Long examId) {
        Exam exam = examMapper.selectById(examId);
        if(exam==null) {
            throw new ServiceImplException(ResultCode.EXAM_NOT_EXISTS);
        }
        return exam;
    }
    private void batchInsertExamQuestion(ExamQuestionDTO examQuestionDTO) {
        int num=1;
        List<ExamQuestion> examQuestionList = new ArrayList<>();
        for(Long questionId : examQuestionDTO.getQuestionIdSet()){
            ExamQuestion examQuestion = new ExamQuestion();
            examQuestion.setExamId(examQuestionDTO.getExamId());
            examQuestion.setQuestionId(questionId);
            examQuestion.setQuestionOrder(num++);
            examQuestionList.add(examQuestion);
        }
        saveBatch(examQuestionList);
    }

    private void checkExamSaveParams(ExamAddDTO examAddDTO,Long examId) {
        List<Exam> examList = examMapper
                .selectList(new LambdaQueryWrapper<Exam>()
                        //这里查询的时候要带上examId
                        .eq(Exam::getTitle, examAddDTO.getTitle())
                        .ne(examId!=null,Exam::getExamId, examId));
        if(CollectionUtil.isNotEmpty(examList)){
            throw new ServiceImplException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        if(examAddDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceImplException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);
        }
        if(examAddDTO.getStartTime().isAfter(examAddDTO.getEndTime())) {
            throw new ServiceImplException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);
        }
    }
}
